#region Imported Namespaces
using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;
using System.Text;

using BO = PHSRAG.Insight21.BusinessObjects;
using PHSRAG.Insight21.InsightUtilities;
using DataAccess = PHSRAG.Utility.DataAccess;
using ICachePolicy = PHSRAG.Insight21.Policy.ICachePolicy;
using SearchCriteria = PHSRAG.Insight21.SearchContext.SearchCriteria;
using GenerateKey = PHSRAG.Insight21.BusinessObjects.ModuleKey;
#endregion

namespace PHSRAG.Insight21.Agreements
{
	/// <summary>
	/// The AgreementSummaries object abstracts the notion of agreement summary. It supports loading of summary information
	/// for all agreements to which the user has access.
	/// </summary>
	public class AgreementSummaries : BO.BusinessObject
	{
		#region Constants
		private static string	AgreementSummariesKey = GenerateKey.MakeKey("AgreementSummaries_574E3124-0394-4277-889C-613D4151ED2B",GenerateKey.Agreement);
		private const string	FilteredDataTableName = "filtered";
		private const string	OriginalDataTableName = "original";
		#endregion

		#region Instance Variables
		private Hashtable		agreementSummaryMap = new Hashtable();
		private string			currentDataTableName = OriginalDataTableName;
		private bool			sortAscending = true;
		private string			sortColumn = "ProjectEndDate";	// Keep this in sync with what the stored proc orders by.
		#endregion

		#region Constructor
		/// <summary>
		/// Initilize the base class with the specified cache policy.
		/// </summary>
		/// <param name="cachePolicy"></param>
		public AgreementSummaries() : base(null)
		{
		}
		#endregion

		#region Public Properties
		/// <summary>
		/// Get the key to be used in all caching operations for this instance of the object.
		/// </summary>
		public static string CacheKey	{ get { return AgreementSummariesKey; } }

		/// <summary>
		/// Get the total number of agreements.
		/// </summary>
		public int Count
		{
			get { return (DataContainer.Tables.Count == 0) ? 0 : DataContainer.Tables[0].Rows.Count; }
		}

		/// <summary>
		/// Get the data for the Report Download
		/// </summary>
		public DataTable ReportData
		{
			get
			{
				return DataContainer.Tables[currentDataTableName];
			}
		}

		/// <summary>
		/// Get the sort sequence.
		/// </summary>
		public bool SortAscending
		{
			get { return sortAscending; }
		}

		/// <summary>
		/// Get the name of the sort column.
		/// </summary>
		public string SortColumn
		{
			get { return sortColumn; }
		}
		#endregion

		#region BusinessObject Methods
        private bool IsDate(string sdate)
        {
            try
            {
                DateTime.Parse(sdate);
                return true;
            }
            catch
            {
            }
            return false;
        }

		/// <summary>
		/// Load the current instancxe with summary data for the current user.
		/// </summary>
		/// <param name="connectionString">Database connection string</param>
		/// <param name="args">Is expected to contain a single argument - the domain user ID</param>
		public override void Load(string connectionString, params object[] args)
		{
			if (args[0] == null )
				throw new Exception("AgreementSummaries load requires search criteria containing at least a domain user ID.");

			SearchCriteria searchCriteria = (SearchCriteria) args[0];
			string sessionID = (string)args[1];

            if ((searchCriteria[Constants.ProjectStartDateKey] != null) && !IsDate(searchCriteria[Constants.ProjectStartDateKey]))
                throw new Exception("Project Start Date contains an invalid date.");

            if ((searchCriteria[Constants.ProjectEndDateKey] != null) && !IsDate(searchCriteria[Constants.ProjectEndDateKey]))
                throw new Exception("Project End Date contains an invalid date.");
					
            if ((searchCriteria[Constants.BudgetStartDateKey] != null) && !IsDate(searchCriteria[Constants.BudgetStartDateKey]))
                throw new Exception("Budget Start Date contains an invalid date.");

            if ((searchCriteria[Constants.BudgetEndDateKey] != null) && !IsDate(searchCriteria[Constants.BudgetEndDateKey]))
                throw new Exception("Budget End Date contains an invalid date.");
            
            if ((searchCriteria[Constants.ProjectEndFromKey] != null) && (searchCriteria[Constants.ProjectEndToKey] != null))
                if (DateTime.Compare(DateTime.Parse(searchCriteria[Constants.ProjectEndFromKey]), DateTime.Parse(searchCriteria[Constants.ProjectEndToKey])) > 0)
                    throw new Exception("Project End From Date cannot be after Project End To Date.");
            
            if ((searchCriteria[Constants.ProjectStartDateKey] != null) && (searchCriteria[Constants.ProjectEndDateKey] != null))
                if (DateTime.Compare(DateTime.Parse(searchCriteria[Constants.ProjectStartDateKey]),DateTime.Parse(searchCriteria[Constants.ProjectEndDateKey])) > 0)
                    throw new Exception("Project Start From Date cannot be after Project Start To Date.");

            if ((searchCriteria[Constants.BudgetStartDateKey] != null) && (searchCriteria[Constants.BudgetEndDateKey] != null))
                if (DateTime.Compare(DateTime.Parse(searchCriteria[Constants.BudgetStartDateKey]),DateTime.Parse(searchCriteria[Constants.BudgetEndDateKey])) > 0)
                    throw new Exception("Budget Start From Date cannot be after Budget Start To Date.");
            
            if ((searchCriteria[Constants.BudgetEndFromKey] != null) && (searchCriteria[Constants.BudgetEndToKey] != null))
                if (DateTime.Compare(DateTime.Parse(searchCriteria[Constants.BudgetEndFromKey]), DateTime.Parse(searchCriteria[Constants.BudgetEndToKey])) > 0)
                    throw new Exception("Budget End From Date cannot be after Budget End To Date.");

			if(DataContainer != null)
				DataContainer.Reset();

			DataContainer =	DataAccess.GetDataSet(connectionString, "GetAgreementSummaries",
				DataAccess.BuildSqlParameterArray(
				"@SessionID", SqlDbType.VarChar,sessionID,
				"@domainUserID", SqlDbType.Int, searchCriteria[Constants.DomainUserIDKey]
                ,"@includeProgramProjects", SqlDbType.Bit, searchCriteria[Constants.IncludeProgramProjectsKey]
				,"@fundNumber", SqlDbType.VarChar, searchCriteria[Constants.FundIDKey]
				,"@folderNumber", SqlDbType.VarChar, searchCriteria[Constants.FolderNumberKey]
				,"@bicsNumber", SqlDbType.VarChar, searchCriteria[Constants.BicsNumberKey]
				,"@walkerNumber", SqlDbType.VarChar, searchCriteria[Constants.WalkerNumberKey]
				,"@sponsorNumber", SqlDbType.VarChar, searchCriteria[Constants.SponsorNumberKey]
				,"@sponsorName", SqlDbType.VarChar, searchCriteria[Constants.SponsorNameKey]
				,"@sponsorTypeId", SqlDbType.Int, searchCriteria[Constants.SponsorTypeIDKey]
				,"@agreementTypeId", SqlDbType.Int, searchCriteria[Constants.AgreementTypeIDKey]
				,"@activityCode", SqlDbType.VarChar, searchCriteria[Constants.ActivityCodeKey]
				,"@locationId", SqlDbType.Int, searchCriteria[Constants.LocationIDKey]
				,"@budgetStartDate", SqlDbType.DateTime, searchCriteria[Constants.BudgetStartDateKey]
				,"@budgetEndDate", SqlDbType.DateTime, searchCriteria[Constants.BudgetEndDateKey]
                ,"@budgetEndFrom", SqlDbType.DateTime, searchCriteria[Constants.BudgetEndFromKey]
                ,"@budgetEndTo", SqlDbType.DateTime, searchCriteria[Constants.BudgetEndToKey]   
				,"@projectStartDate", SqlDbType.DateTime, searchCriteria[Constants.ProjectStartDateKey]
				,"@projectEndDate", SqlDbType.DateTime, searchCriteria[Constants.ProjectEndDateKey]
                ,"@projectEndFrom", SqlDbType.DateTime, searchCriteria[Constants.ProjectEndFromKey]
                ,"@projectEndTo", SqlDbType.DateTime, searchCriteria[Constants.ProjectEndToKey]
                ,"@projectTitle", SqlDbType.VarChar, searchCriteria[Constants.ProjectTitle]
				,"@piName", SqlDbType.VarChar, searchCriteria[Constants.PIIDKey]
				,"@chiefCodeList", SqlDbType.VarChar, searchCriteria.COSIds
				,"@sponsorGroupTypeId", SqlDbType.Int, searchCriteria[Constants.ViewingOptionKey]
				,"@budgetEndingDays", SqlDbType.Int, searchCriteria[Constants.DaysBudgetEndingKey]
				,"@projectEndingDays", SqlDbType.Int, searchCriteria[Constants.DaysProjectEndingKey]
				,"@daysFundCreated", SqlDbType.Int, searchCriteria[Constants.DaysFundCreatedKey]
				,"@lowBalance", SqlDbType.Money, searchCriteria[Constants.BalanceGreaterThanKey]
				,"@highBalance", SqlDbType.Money, searchCriteria[Constants.BalanceLessThanKey]
				,"@agreementStatusTypeId", SqlDbType.VarChar, searchCriteria[Constants.AgreementStatusIDKey]
				,"@fundStatusTypeId", SqlDbType.Int, searchCriteria[Constants.FundStatusIDKey]
                ,"@viewTypeId", SqlDbType.Int, searchCriteria[Constants.ViewType]

				));

			if ( DataContainer.Tables.Count == 0 )
				throw new Exception("Failed to load Agreement Summary information.");

			DataContainer.Tables[0].TableName = OriginalDataTableName;

			DataView dataView = new DataView(DataContainer.Tables[OriginalDataTableName]);
			dataView.Sort = String.Format("{0} {1}",SortColumn, SortAscending ? Constants.SortViewAscending: Constants.SortViewDescending);
			dataView.RowStateFilter = DataViewRowState.CurrentRows;

			DataTable dataTable = dataView.Table.Clone();
			dataTable.TableName = OriginalDataTableName;

			foreach (DataRowView dataRowView in dataView)
				dataTable.ImportRow(dataRowView.Row);

			DataContainer.Tables.Remove(OriginalDataTableName);
			DataContainer.Tables.Add(dataTable);
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Get the list of agreement objects bound by a start and end index. These indeces are into the list of agreement
		/// summaries that was loaded during the Load() method execution into the data container. Typically, the start index
		/// is on a "page" boundary, where the "page" referes to a datagrid page.
		/// Note that the total number of agreement objects returned may be less than the requested count.
		/// We maintain a Hashtable of ArrayList objects using the start index as the key. This allows us to retrieve the
		/// set of agreeement objects associated with that index, thereby directly supporting random access based the
		/// page index.
		/// If such an ArrayList does not exist for the current page index, we load them using the list of agreement IDs.
		/// The start end end indeces into that list are computed based on the current page index and the datagrid page size.
		/// Agreement objects are loaded based on the summaries which falls within the compute start and end indeces, 
		/// and saved in an ArrayList. That ArrayList is then stored in the Hashtable with the current page index as its
		/// key.
		/// </summary>
		/// <param name="startIndex">Index within the agreement summary list to begin at</param>
		/// <param name="count">Total number of agreement objects to load</param>
		/// <returns>The list of agreement objects</returns>
		public ArrayList GetAgreementList(int startIndex, int count)
		{
			ArrayList agreementList = agreementSummaryMap[startIndex] as ArrayList;
			if (agreementList == null)
			{
				agreementList = new ArrayList();
				DataTable dataTable = DataContainer.Tables[currentDataTableName];
				for (int i = startIndex, end = Math.Min(i + count, dataTable.Rows.Count); i < end; ++i)
					agreementList.Add(new AgreementSummary(CachePolicy, dataTable.Rows[i]));
				agreementSummaryMap[startIndex] = agreementList;
			}
			return agreementList;
		}

		/// <summary>
		/// Search the DataContainer for rows which match the specified search criteria.
		/// Note that we maintain two DataTables within the DataContainer; a full-complement DataTable and a filtered one.
		/// This allows us to quickly apply various search criteria without having to hit the database. If a filter is in
		/// effect, we simply set the value of the currentTableIndex to DataTableIndex.Filtered so that the
		/// GetAgreementList() method may retreive from the appropriate DataTable.
		/// </summary>
		/// <param name="searchCriteria"></param>
		/// <remarks>True if the search criteria did any filtering; false otherwise</remarks>
		public bool Search(params object[] searchArgs)
		{
			agreementSummaryMap.Clear();
			Load(Utility.DataAccess.ConnectionString, searchArgs);

			//@@@TODOJGC revisit signature of function...for right now always return true until design is flushed out.
			return true;
		}

		/// <summary>
		/// Sort the list of agreement summaries by the speicified column.
		/// If the column is to be sorted is the same as what was last sorted on, the sort sequence is toggled. Else, the
		/// sort sequence is set to ascending.
		/// </summary>
		/// <param name="columnName">Column to sort by</param>
		public void Sort(string columnName)
		{
			agreementSummaryMap.Clear();

			sortAscending = (sortColumn == columnName) ? !sortAscending : true;
			sortColumn = columnName;

			DataTable dataTable = DataContainer.Tables[currentDataTableName];
			DataView dataView = dataTable.DefaultView;
			dataView.Sort = string.Format("{0} {1}", columnName, sortAscending ? "ASC" : "DESC");

			DataTable sortedDataTable = dataTable.Clone();
			foreach (DataRowView rowView in dataView)
				sortedDataTable.ImportRow(rowView.Row);

			DataContainer.Tables.Remove(currentDataTableName);
			DataContainer.Tables.Add(sortedDataTable);
		}
		#endregion

		#region Private Methods
		private void AddFilterCriteria(StringBuilder filter, string filterName, string filterValue)
		{
			filter.AppendFormat("{0} {1}='{2}'", (filter.Length > 0) ? "AND" : "", filterName, filterValue);
		}
		#endregion
	}
}

// --- EOF ---
